package com.cenxi.manage;


import cn.hutool.crypto.digest.MD5;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cenxi.common.dto.LoginDTO;
import com.cenxi.common.dto.MenuBusiDTO;
import com.cenxi.common.dto.RoleBusiDTO;
import com.cenxi.common.result.Resp;
import com.cenxi.config.RedisService;
import com.cenxi.dao.entity.*;
import com.cenxi.dao.mapper.AccountRoleMapper;
import com.cenxi.dao.mapper.MenuMapper;
import com.cenxi.dao.mapper.RoleMapper;
import com.cenxi.dao.mapper.RoleMenuMapper;
import com.cenxi.dao.service.AccountService;
import com.cenxi.utils.constants.Constants;
import com.cenxi.utils.jwt.JwtUtils;
import com.cenxi.utils.subject.LoginSubject;
import com.cenxi.utils.subject.SecurityContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author cenxi
 * @Date 2023/8/16 17:33
 * @Version 1.0
 */
@Slf4j
@Component
public class ManageLoginService {
    @Autowired
    private RoleMenuMapper roleMenuMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private AccountRoleMapper accountRoleMapper;
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private AccountService accountService;
    @Autowired
    private RedisService redisService;

    static final String ACCOUNT_PWD_ERR_CNT_KEY = "busi_account:pwd_err_cnt:";
    /**
     * 密码最大错误次数
     */
    static final int PASSWORD_MAX_RETRY_COUNT = 5;

    /**
     * 密码锁定时间，默认10（分钟）
     */
    static final long PASSWORD_LOCK_TIME = 10;

    public Resp<LoginSubject> login(LoginDTO data) {
        if (Objects.isNull(data) || Objects.isNull(data.getAccount()) || Objects.isNull(data.getPassword())) {
            return Resp.error("参数有误");
        }
        Account account = accountService.getOne(new LambdaQueryWrapper<Account>()
                .eq(Account::getAccount, data.getAccount()));
        if (Objects.isNull(account)) {
            return Resp.error("账户不存在");
        }

        if (!account.getPassword().equals(new MD5().digestHex16(data.getPassword()))) {
            Long increment = redisService.increment(ACCOUNT_PWD_ERR_CNT_KEY + data.getAccount(), PASSWORD_LOCK_TIME, TimeUnit.MINUTES);
            if (increment > PASSWORD_MAX_RETRY_COUNT) {
                return  Resp.error("密码错误，请"+ PASSWORD_LOCK_TIME +"分钟后重试");
            }
            return Resp.error("密码错误,剩余: " + (PASSWORD_MAX_RETRY_COUNT - increment) + "次登录机会");
        }else {
            // 清除错误重试次数
            redisService.deleteObject(ACCOUNT_PWD_ERR_CNT_KEY + data.getAccount());
        }
        // 校验账户状态
        if (account.getState() == Constants.DISABLE) {
            return Resp.error("账户已被封禁,请联系管理员");
        }
        // 生成token返回前端
        LoginSubject loginUserDTO = new LoginSubject();
        loginUserDTO.setAccountId(account.getId());
        loginUserDTO.setAccount(account.getAccount());
        loginUserDTO.setAvatar(account.getAvatar());
        loginUserDTO.setNickName(account.getNickName());
        setLoginSubject(loginUserDTO);
        Map<String, Object> tokenDTO = JwtUtils.createToken(loginUserDTO);
        String accessToken = (String) tokenDTO.get("access_token");
        loginUserDTO.setToken(accessToken);
        loginUserDTO.setLoginTime(LocalDateTime.now());
        // 添加token信息到缓存中
        redisService.setCacheObject(Constants.LOGIN_TOKEN_KEY + accessToken, loginUserDTO, Constants.OUT_TIME, TimeUnit.SECONDS);
        account.setLoginTime(LocalDateTime.now());
        accountService.updateById(account);
        return Resp.succeed(loginUserDTO);
    }
    /**
     * 填充登录信息进入缓存
     */
    public void setLoginSubject(LoginSubject loginUserDTO) {
        Long accountId = loginUserDTO.getAccountId();
        List<Long> roleIds = accountRoleMapper.selectList(new LambdaQueryWrapper<AccountRole>()
                .eq(AccountRole::getAccountId, accountId)).stream().map(AccountRole::getRoleId).collect(Collectors.toList());
        List<Role> roles = roleMapper.selectBatchIds(roleIds);
        loginUserDTO.setRoles(roles.stream().map(item -> {
            RoleBusiDTO roleBusiDTO = new RoleBusiDTO();
            roleBusiDTO.setRoleName(item.getRoleName());
            roleBusiDTO.setId(item.getId());
            return roleBusiDTO;
        }).collect(Collectors.toList()));
        List<Long> menuIds = roleMenuMapper.selectList(new LambdaQueryWrapper<RoleMenu>()
                .in(RoleMenu::getRoleId, roleIds)).stream().map(RoleMenu::getMenuId).collect(Collectors.toList());
        List<Menu> menus = menuMapper.selectBatchIds(menuIds);
        loginUserDTO.setPermission(menus.stream().map(Menu::getPerms).filter(Objects::nonNull).collect(Collectors.toList()));
        loginUserDTO.setMenus(getChildren(menus));
        log.info("111111登录信息填充成功111111");
    }

    public List<MenuBusiDTO> getChildren(List<Menu> menus) {
        List<MenuBusiDTO> collect = menus.stream().map(item -> {
            MenuBusiDTO menuBusiDTO = new MenuBusiDTO();
            menuBusiDTO.setMenuName(item.getMenuName());
            menuBusiDTO.setId(item.getId());
            menuBusiDTO.setComponent(item.getComponent());
            menuBusiDTO.setParentId(item.getParentId());
            menuBusiDTO.setPath(item.getPath());
            Map<String, Object> map = new HashMap<>();
            map.put("title", item.getMenuName());
            map.put("icon", item.getIcon());
            menuBusiDTO.setMeta(map);
            menuBusiDTO.setRedirect(item.getPath());
            menuBusiDTO.setName(item.getMenuName());
            menuBusiDTO.setAlwaysShow(Boolean.TRUE);
            return menuBusiDTO;
        }).collect(Collectors.toList());
        Map<Long, MenuBusiDTO> map = collect.stream().collect(Collectors.toMap(MenuBusiDTO::getId, a -> a));
        List<MenuBusiDTO> resList = new ArrayList<>();
        collect.forEach(v -> {
            if (Objects.isNull(v.getParentId())) {
                resList.add(map.get(v.getId()));
            } else {
                MenuBusiDTO menu = map.get(v.getParentId());
                if (Objects.nonNull(menu)) {
                    List<MenuBusiDTO> children = menu.getChildren();
                    children.add(v);
                } else {
                    resList.add(v);
                }
            }
        });
        return resList;
    }


    public Resp<Void> logout() {
        String token = SecurityContextUtil.getToken();
        if (StringUtils.isNotEmpty(token)) {
            // 删除用户缓存记录
            redisService.deleteObject(Constants.LOGIN_TOKEN_KEY + token);
            return Resp.succeed();
        }
        return Resp.error("token不存在");
    }
}
